<?php

namespace App\Command\ResumeParsing\Service;

use \DOMDocumemt;
use \DOMDocumemt\DOMNode;
use EchoPHP\Facades\Log;

define('SALARY_DEFAULT_MONTH_COUNT', 13);    //年薪默认薪资月数

//读简历文件类
class ParsingFunction {

    private $name;

    function __construct() {
        //$this->name = "MyDestructableClass";
    }

    function __destruct() {
        //print "Destroying " . $this->name . "\n";
    }

    //初始化简历结构
    function resumeInit() {
        $resumeInitArr = array();
        //简历来源
        $resumeInitArr['source'] = '';

        //简历ID
        $resumeInitArr['id'] = '';
        $resumeInitArr['id_p'] = '';

        //简历活跃时间
        $resumeInitArr['time'] = '';
        $resumeInitArr['time_p'] = '';

        //性别
        $resumeInitArr['sex'] = '';
        $resumeInitArr['sex_code'] = 2; //保密
        $resumeInitArr['sex_p'] = '';

        //民族
        $resumeInitArr['nation'] = '';
        $resumeInitArr['nation_p'] = '';

        //身份证号码
        $resumeInitArr['identitycard'] = '';
        $resumeInitArr['identitycard_p'] = '';

        //政治情况：党员、团员
        $resumeInitArr['politic'] = '';
        $resumeInitArr['politic_code'] = '';
        $resumeInitArr['politic_p'] = '';

        //手机电话
        $resumeInitArr['mobile'] = '';
        $resumeInitArr['mobile_p'] = '';

        //电子邮箱
        $resumeInitArr['email'] = '';
        $resumeInitArr['email_p'] = '';

        //年龄
        $resumeInitArr['age'] = '';
        $resumeInitArr['age_p'] = '';

        //生日
        $resumeInitArr['birthday'] = '';
        $resumeInitArr['birthday_p'] = '';

        //最近工作单位
        $resumeInitArr['company'] = '';
        $resumeInitArr['company_p'] = '';

        //姓名
        $resumeInitArr['name'] = '';
        $resumeInitArr['name_p'] = '';

        //工作年限
        $resumeInitArr['workyear'] = '';
        $resumeInitArr['workyear_code'] = '';
        $resumeInitArr['workyear_p'] = '';

        //户籍城市
        $resumeInitArr['area'] = '';
        $resumeInitArr['area_code'] = '';
        $resumeInitArr['area_p'] = '';

        //目前所在城市
        $resumeInitArr['workarea'] = '';
        $resumeInitArr['workarea_code'] = '';
        $resumeInitArr['workarea_p'] = '';

        //期望工作城市
        $resumeInitArr['expectarea'] = '';
        $resumeInitArr['expectarea_code'] = '';
        $resumeInitArr['expectarea_p'] = '';

        //目前薪资（月薪/元）
        $resumeInitArr['salary'] = '';
        //$resumeInitArr['salary_code'] = '';

        $resumeInitArr['salary_p'] = '';
        $resumeInitArr['salaryfrom'] = '';
        $resumeInitArr['salaryend'] = '';

        //期望薪资（月薪/元）
        $resumeInitArr['expectsalary'] = '';
        //$resumeInitArr['expectsalary_code'] = '';
        $resumeInitArr['expectsalary_p'] = '';
        $resumeInitArr['expectsalaryfrom'] = '';
        $resumeInitArr['expectsalaryend'] = '';

        //学历
        $resumeInitArr['degree'] = '';
        $resumeInitArr['degree_code'] = '';
        $resumeInitArr['degree_p'] = '';

        //大学专业
        $resumeInitArr['major'] = '';
        $resumeInitArr['major_code'] = '';
        $resumeInitArr['major_p'] = '';

        //职能
        $resumeInitArr['position'] = '';
        $resumeInitArr['position_p'] = '';

        //所在行业
        $resumeInitArr['industry'] = '';
        $resumeInitArr['industry_code'] = '';
        $resumeInitArr['industry_p'] = '';

        //职业
        $resumeInitArr['career'] = '';
        $resumeInitArr['career_p'] = '';

        //婚姻情况
        $resumeInitArr['married'] = '';
        $resumeInitArr['married_code'] = '';
        $resumeInitArr['married_p'] = '';

        //工作类型(全职/兼职)
        $resumeInitArr['jobterm'] = '';
        $resumeInitArr['jobterm_code'] = '';
        $resumeInitArr['jobterm_p'] = '';

        //求职状态
        $resumeInitArr['current_situation'] = '';
        $resumeInitArr['current_situation_code'] = '';
        $resumeInitArr['current_situation_p'] = '';

        //到岗时间
        $resumeInitArr['entrytime'] = '';
        $resumeInitArr['entrytime_code'] = '';
        $resumeInitArr['entrytime_p'] = '';

        //学校
        $resumeInitArr['school'] = '';
        $resumeInitArr['school_p'] = '';

        //勿推企业
        $resumeInitArr['nofocuse'] = '';
        $resumeInitArr['nofocuse_p'] = '';

        //工作经历
        $resumeInitArr['workinfo'] = array();      //array('time'=>'','time_from'=>'','time_end'=>'','company'=>'','duty'=>'','salary'=>'','comment'=>'');
        $resumeInitArr['workinfo_p'] = '';

        //项目经验
        $resumeInitArr['projectinfo'] = array();   //array('time'=>'','time_from'=>'','time_end'=>'','project'=>'','company'=>'','duty'=>'','comment'=>'');
        $resumeInitArr['projectinfo_p'] = '';

        //教育经历
        $resumeInitArr['educationinfo'] = array(); //array('time'=>'','time_from'=>'','time_end'=>'','school'=>'','major'=>'','degree'=>'','comment'=>'');
        $resumeInitArr['educationinfo_p'] = '';

        //技能特长
        $resumeInitArr['skillinfo'] = '';
        $resumeInitArr['skillinfo_p'] = '';

        //奖项荣誉
        $resumeInitArr['awardinfo'] = '';
        $resumeInitArr['awardinfo_p'] = '';

        //自我评价
        $resumeInitArr['commentinfo'] = '';
        $resumeInitArr['commentinfo_p'] = '';

        //头像图片路径
        $resumeInitArr['avatar'] = '';

        //简历处理后，其他信息集中堆积这里
        $resumeInitArr['otherinfo'] = '';
        $resumeInitArr['otherinfo_p'] = '';
        return $resumeInitArr;
    }

    //
    function delPResume(&$resumeInfo) {
        foreach ($resumeInfo as $key => $value) {
            if ($this->endWith($key, '_p') === false && !in_array($key, array('resumeBase', 'resumeAtom', 'resumeSection', 'resumeDom'))) {
            } else {
                unset($resumeInfo[$key]);
            }
        }
    }

    //
    function printResume($resumeInfo) {
        foreach ($resumeInfo as $key => $value) {
            echo "<table>";
            if ($this->endWith($key, '_p') === false && !in_array($key, array('resumeBase', 'resumeAtom', 'resumeSection', 'resumeDom'))) {
                if (is_string($value)) {
                    echo "<tr><td>key=" . $key . "_</td><td>value=" . print_img($value, true) . "_</td><td>" . @$resumeInfo[$key . '_p'] . "</td></tr>";
                } else if ($key == 'ocrImgFile') {  //已弃用
                    echo "<tr><td>key=" . $key . "_</td><td>value=内容图片_</td><td></td></tr>";
                } else {
                    echo "<tr><td>key=" . $key . "_</td><td>value=" . print_img($value, true) . "_</td><td></td></tr>";
                }
            }
            echo "</table>";
        }
    }

    //格式化文本
    function formatTxt($tmpValue, $removeCrLf = FALSE) {
        /*经测试，此段所述空格均能被\s正则匹配，故统一处理
        //但是不间断空格有个问题，就是它无法被trim()所裁剪，也无法被正则表达式的\s所匹配，
        //也无法被StringUtils的isBlank()所识别，也就是说，无法像裁剪寻常空格那样移除这个不间断空格。
        //我们可以利用不间断空格的Unicode编码来移除它，其编码为\u00A0。
        $pattern = "/[\x{00A0}]+/u";                                     // \x{00A0}=chr(160)
        $tmpValue = @mb_ereg_replace($pattern, "", $tmpValue);
        //$tmpValue = str_replace(" ", " ", $tmpValue);  //不间断空格\u00a0空格," "例子
        $tmpValue = str_replace(array("&nbsp;", "　", " "), " ", $tmpValue); // "　"=chr(12288)
        $tmpValue = trim($tmpValue);
        */

        //多个连续空格只保留1个，特殊空格（换行符除外）转为普通空格
        $tmpValue = preg_replace("/[^\S\r\n]+/u", " ", $tmpValue);

        if ($removeCrLf === TRUE) {    //删除换行
            $tmpValue = preg_replace("/(?:\s*(?:\r\n|[\r\n])\s*)/u", "", $tmpValue);
        } else {    //多个连续换行只保留1个，行首尾空格删除
            $tmpValue = preg_replace("/(?:\s*(?:\r\n|[\r\n])\s*)+/u", "\r\n", $tmpValue);
        }

        if ($removeCrLf === 'space') {    //换行改为空格
            $tmpValue = preg_replace("/(?:\s*(?:\r\n|[\r\n])\s*)+/u", " ", $tmpValue);
        } elseif ($removeCrLf == 'auto') {
            $tmpValue = preg_replace("/(?<=[^。：；.:;])\s*?(?:\r\n|[\r\n])+\s*(?!\d+[、.])/u", "", $tmpValue);
        }

        if ($this->startWith($tmpValue, "[if gte vml 1]>") && $this->endWith($tmpValue, "<![endif]")) {
            //echo ('<br/><br/>'.$treeIndex.'_'.'html异常内容');
            return '';
        }
        return trim($tmpValue);
    }

    function readNum($numText) {
        if (preg_match('/(?<base>\d+(?:\.\d+)?)\s*(?<multiplier>[kw万])?/u', $numText, $matches)) {
            if (!isset($matches['multiplier'])) return $matches['base'];
            switch ($matches['multiplier']) {
                case 'k':
                    return $matches['base'] * 1000;
                case 'w':
                case '万':
                    return $matches['base'] * 10000;
                default:
                    return $matches['base'];
            }
        }
        return $numText;
    }

    function parseSalary($text, $hint = 'monthly') {
        $result = array();
        if (preg_match('/(?<begin>\d+(?:\.\d+)?\s*(?<bMul>[kw万])?)(?<bUnit>\s*元)?(?:\s*[-–—~～至到]\s*(?<end>\d+(?:\.\d+)?\s*(?<eMul>[kw万])?)\s*元?)?\s*[\/每]\s*年/u', $text, $matches)) {
            $result['annual'] = $this->internalParseSalary($matches);
        }
        if (preg_match('/(?<begin>\d+(?:\.\d+)?\s*(?<bMul>[kw万])?)(?<bUnit>\s*元)?(?:\s*[-–—~～至到]\s*(?<end>\d+(?:\.\d+)?\s*(?<eMul>[kw万])?)\s*元?)?\s*(?:(?:(?:\/|每个?)\s*月)?\s*(?:乘以|[*×x乘])?\s*(?<monthCount>\d{2})\s*(?:个?月份?|薪)|(?:\/|每个?)\s*月)/u', $text, $matches)) {
            $result['monthly'] = $this->internalParseSalary($matches);
            $result['monthCount'] = !empty($matches['monthCount']) ? $matches['monthCount'] : SALARY_DEFAULT_MONTH_COUNT;
        }
        if (!count($result) && preg_match('/(?<begin>\d+(?:\.\d+)?\s*(?<bMul>[kw万])?)(?<bUnit>\s*元)?(?:\s*[-–—~～至到]\s*(?<end>\d+(?:\.\d+)?\s*(?<eMul>[kw万])?)\s*元?)?/u', $text, $matches)) {
            if ($hint == 'annual') {
                $result['annual'] = $this->internalParseSalary($matches);
            } else {
                $result['monthly'] = $this->internalParseSalary($matches);
                $result['monthCount'] = SALARY_DEFAULT_MONTH_COUNT;
            }
        }
        if (!count($result)) return $result;
        if (empty($result['monthCount'])) $result['monthCount'] = SALARY_DEFAULT_MONTH_COUNT;
        if (!isset($result['annual'])) {
            $result['annual'] = array('begin' => $result['monthly']['begin'] * $result['monthCount'], 'end' => $result['monthly']['end'] * $result['monthCount']);
        } elseif (!isset($result['monthly'])) {
            $result['monthly'] = array('begin' => intval($result['annual']['begin'] / $result['monthCount'] / 100) * 100, 'end' => intval($result['annual']['end'] / $result['monthCount'] / 100) * 100);
        }
        return $result;
    }

    private function internalParseSalary($matches) {
        $result['begin'] = intval($this->readNum($begin = $matches['begin']));
        $result['end'] = intval($this->readNum($end = (!empty($matches['end']) ? $matches['end'] : $matches['begin'])));
        if ($result['begin'] * 1000 < $result['end'] && empty($matches['bUnit']) && empty($matches['bMul']) && !empty($matches['eMul'])) {
            if (($tmp = $this->readNum($begin . $matches['eMul'])) < $result['end']) {
                $result['begin'] = intval($tmp);
            }
        }
        return $result;
    }

    //指定字符串切分
    function spiltToArry($str, $spiltStr) {
        if (empty($str) || strlen($str) < 2) {
            return array();
        }
        $var = explode($spiltStr, $str);
        return $var;
    }

    //char(16)替换成换行符
    function replaceChar16($str, $replaceStr = "\r\n") {
        $str = str_replace(chr(16), $replaceStr, $str);
        return $str;
    }

    //将连续的char(16)保留1个
    function aloneChar16($str) {
        $strExpression = '/\x10{2,}/';
        $str = preg_replace($strExpression, chr(16), $str);
        return $str;
    }

    //字符串开头
    function startWith($str, $needle) {
        return strpos($str, $needle) === 0;
    }

    //字符串结尾
    function endWith($haystack, $needle) {
        $length = strlen($needle);
        if ($length == 0) {
            return true;
        }
        return (substr($haystack, -$length) === $needle);
    }

    //字符转Ascii码
    function encode($c) {
        $scill = '';
        $len = strlen($c);
        $a = 0;
        while ($a < $len) {
            $ud = 0;
            if (ord($c{$a}) >= 0 && ord($c{$a}) <= 127) {
                $ud = ord($c{$a});
                $a += 1;
                $scill .= $c{$a};
            } else if (ord($c{$a}) >= 192 && ord($c{$a}) <= 223) {
                $ud = (ord($c{$a}) - 192) * 64 + (ord($c{$a + 1}) - 128);
                $a += 2;
            } else if (ord($c{$a}) >= 224 && ord($c{$a}) <= 239) {
                $ud = (ord($c{$a}) - 224) * 4096 + (ord($c{$a + 1}) - 128) * 64 + (ord($c{$a + 2}) - 128);
                $a += 3;
            } else if (ord($c{$a}) >= 240 && ord($c{$a}) <= 247) {
                $ud = (ord($c{$a}) - 240) * 262144 + (ord($c{$a + 1}) - 128) * 4096 + (ord($c{$a + 2}) - 128) * 64 + (ord($c{$a + 3}) - 128);
                $a += 4;
            } else if (ord($c{$a}) >= 248 && ord($c{$a}) <= 251) {
                $ud = (ord($c{$a}) - 248) * 16777216 + (ord($c{$a + 1}) - 128) * 262144 + (ord($c{$a + 2}) - 128) * 4096 + (ord($c{$a + 3}) - 128) * 64 + (ord($c{$a + 4}) - 128);
                $a += 5;
            } else if (ord($c{$a}) >= 252 && ord($c{$a}) <= 253) {
                $ud = (ord($c{$a}) - 252) * 1073741824 + (ord($c{$a + 1}) - 128) * 16777216 + (ord($c{$a + 2}) - 128) * 262144 + (ord($c{$a + 3}) - 128) * 4096 + (ord($c{$a + 4}) - 128) * 64 + (ord($c{$a + 5}) - 128);
                $a += 6;
            } else if (ord($c{$a}) >= 254 && ord($c{$a}) <= 255) { //error
                $ud = false;
            }
            $scill .= $ud . ";";
        }
        return $scill;
    }

    // 字符串全角、半角转化
    // 第一个参数：传入要转换的字符串
    // 第二个参数：0-全角转半角；1-半角转全角
    function strUnify($str, $formatType = '0', $noSpace = false) {
        $str = str_replace(" ", " ", $str);    //不间断空格\u00a0空格
        if ($noSpace) {
            $str = str_replace(' ', '', $str); //去除空格
            $str = str_replace('　', '', $str);
        }
        //全角
        $DBC = Array(
            '０', '１', '２', '３', '４', '５', '６', '７', '８', '９',
            'Ａ', 'Ｂ', 'Ｃ', 'Ｄ', 'Ｅ', 'Ｆ', 'Ｇ', 'Ｈ', 'Ｉ', 'Ｊ',
            'Ｋ', 'Ｌ', 'Ｍ', 'Ｎ', 'Ｏ', 'Ｐ', 'Ｑ', 'Ｒ', 'Ｓ', 'Ｔ',
            'Ｕ', 'Ｖ', 'Ｗ', 'Ｘ', 'Ｙ', 'Ｚ', 'ａ', 'ｂ', 'ｃ', 'ｄ',
            'ｅ', 'ｆ', 'ｇ', 'ｈ', 'ｉ', 'ｊ', 'ｋ', 'ｌ', 'ｍ', 'ｎ',
            'ｏ', 'ｐ', 'ｑ', 'ｒ', 'ｓ', 'ｔ', 'ｕ', 'ｖ', 'ｗ', 'ｘ',
            'ｙ', 'ｚ', '－', '　', '：', '．', '，', '／', '％', '＃',
            '！', '＠', '＆', '（', '）', '＜', '＞', '＂', '＇', '？',
            '［', '］', '｛', '｝', '＼', '｜', '＋', '＝', '＿', '＾',
            '￥', '～', '｀', '＊', '。', '；', '＄', '“', '‘', '《',
            '》'
        );
        //半角
        $SBC = Array(
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
            'y', 'z', '-', ' ', ':', '.', ',', '/', '%', '#',
            '!', '@', '&', '(', ')', '<', '>', '"', '\'', '?',
            '[', ']', '{', '}', '\\', '|', '+', '=', '_', '^',
            '$', '~', '`', '*', '.', ';', '$', '"', "'", "<",
            ">"
        );
        if ($formatType == '1') {
            return str_replace($SBC, $DBC, $str); // 半角到全角
        }
        if ($formatType == '0') {
            return str_replace($DBC, $SBC, $str); // 全角到半角
        }

        return false;
    }

    //过滤字符串 '*' '#','@','.'
    function filterStr($strData, $replaceTo = '', $excludeSpace = false) {
        $strData = $this->strUnify($strData);
        $strData = str_replace(array('　'), ' ', $strData);
        $strData = str_replace(chr(16), ' ', $strData);
        $strData = str_replace(array('/', '”', '〈', '〉', ':', '|', '│', '丨', '﹠', '$', '%', '^',
            '&', '、', '，', ',', '·', '《', '》', '`', '’', "'", '+', '"', '\\', '-',
            '(', ')', '[', ']', '{', '}', '<', '>', '~', '!', '【', '】', '?', '=', ';', '_', '—'), $replaceTo, $strData);
        if ($excludeSpace == false) {
            $strData = str_replace(array(' '), $replaceTo, $strData);
        }
        return $strData;
    }

    //调用图片识别接口从图片中获取信息
    function getInfoFromImg($img) {
        //TODO: 开发调试，伪造OCR结果
        if (config('app.resume_machine_learning.fake_ocr_result', false))
            return ['result' => 1, 'message' => ['phone' => 'OCR手机', 'email' => 'OCR Email', 'year' => 'OCR工作年限', 'name' => 'OCR姓名']];
        try {
            $postData = json_encode(['image' => $img]);
            $resultJson = ParsingFunction::getRequestByCurl(OCR_IMGHOST, $postData);
            $result = json_decode($resultJson, true);
            //echo '<img src="data:image/png;base64,' . $img . '"/>';
            //echo var_export($result, true);
            return $result;
        } catch (\Exception $e) {
            Log::error(e_dump($e), [], 'cmd');
            return ['result' => -99999, 'status' => false, 'cname' => '', 'email' => '', 'mobilephone' => ''];
        }
        //return ['status' => true, 'cname' => '梁女士', 'email' => 'aaa@aaa.com', 'mobilephone' => 15912345678];
    }

    /**
     * getRequestByCurl Curl版本
     * 使用方法：
     * $url string     需要Post到的页面(GET的参数加在URL后)
     * $string         需要Post过去的参数
     * $authString     传过来的帐号密码是个数组
     * $extHeader 头部参数
     */
    public static function getRequestByCurl($url, $string = '', $timeout = 30, $authString = '', $needProxy = false, $extHeader = '') {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);                    //请求URL
        if (!empty($string)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $string);      //post参数
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);            //将curl_exec()获取的信息以文件流的形式返回，而不是直接输出
        curl_setopt($ch, CURLOPT_USERAGENT, "SPYNET51JOB");     //在HTTP请求中包含一个"User-Agent: "头的字符串
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);            //设置超时时间（单位：秒）
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);        //不验证证书下同
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);

        if ($needProxy && !SPYNET_ONLINE) {
            curl_setopt($ch, CURLOPT_PROXY, "10.100.10.100"); //代理服务器地址
            curl_setopt($ch, CURLOPT_PROXYPORT, 3128); //代理服务器端口
        }

        $headers = array("Content-Type: application/json", "X-HTTP-Method-Override: post");

        if (!empty($authString)) {
            $headers[] = "Authorization: Basic " . base64_encode($authString['username'] . ":" . $authString['password']);
        }
        if (!empty($extHeader)) {
            $headers[] = $extHeader;
        }
        if (!empty($authString) || !empty($extHeader)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        }
        $data = curl_exec($ch);                                 //执行会话

        if (curl_errno($ch)) {
            //记录http信息
            $info = curl_error($ch);
            $errInfo = var_export($info, TRUE);
            //var_dump('getRequestByCurl()错误信息：' . $errInfo . ' 访问地址：' . $url);
        }
        curl_close($ch);
        return $data;
    }

    //直接返回匹配到指定正则的文本片段，未匹配返回FALSE
    //options可配置参数如下
    //groupName：字符串 - 返回匹配指定组名，否则匹配整个正则
    //trim：TRUE - 常规trim()结果，字符串 - trim()指定字符，否则不trim()。默认为TRUE。
    //format：0 - formatTxt(removeCrLf = FALSE)，1 - formatTxt(removeCrLf = 'auto')，2 - formatTxt(removeCrLf = 'space')，3 - formatTxt(removeCrLf = TRUE)，否则不格式化。默认为1。
    function preg_get($pattern, $subject, $options = array()) {
        if (!isset($options['format']) || $options['format'] === 1) {
            $subject = $this->formatTxt($subject);
        } elseif ($options['format'] === 2) {
            $subject = $this->formatTxt($subject, 'auto');
        } elseif ($options['format'] === 3) {
            $subject = $this->formatTxt($subject, 'space');
        } elseif ($options['format'] === 4) {
            $subject = $this->formatTxt($subject, TRUE);
        }
        if (!preg_match($pattern, $subject, $matches)) return FALSE;
        $index = isset($options['groupName']) && is_string($options['groupName']) ? $options['groupName'] : 'result';
        if ($index !== 0 && !isset($matches[$index])) $index = 0;
        if (!isset($matches[$index])) return FALSE;
        $result = $matches[$index];
        if (!isset($options['trim']) || $options['trim'] === TRUE) {
            $result = trim($result);
        } elseif (is_string($options['trim'])) {
            $result = trim($result, $options['trim']);
        }
        return $result;
    }

    function parseTimespan($text, $education = false) {
        $result = array('begin' => '', 'end' => '');
        if (preg_match('/(?<!\d)(?<begin>(?:19|20)?\d{2}\s*年\s*[01]?\d\s*月(?<beginDate>\s*[0-3]?\d[日号])?|(?:19|20)?\d{2}\s*(?<delimiter>[\-\.\/\\\\])\s*[01]?\d(?:\k<delimiter>\s*[0-3]?\d)?)(?!\d)(?:\s*(?:(?=至今)|[-–—\/\\\\~～到至]\s*)(?<end>至今|现在|(?(delimiter)(?:19|20)?\d{2}\s*\k<delimiter>\s*[01]?\d(?:\k<delimiter>\s*[0-3]?\d)?|(?:19|20)?\d{2}\s*年\s*[01]?\d\s*月(?<endDate>\s*[0-3]?\d[日号])?))(?!\d))?/u', $text, $matches)) {
            $delimiter = !empty($matches['delimiter']) ? $matches['delimiter'] : '';
            $result['begin'] = $this->formatDate($matches['begin'], $delimiter);
            $result['end'] = !empty($matches['end']) ? $this->formatDate($matches['end'], $delimiter, !empty($matches['endDate']) ? FALSE : TRUE) : $this->formatDate($matches['begin'], $delimiter, !empty($matches['beginDate']) ? FALSE : TRUE);
        } elseif (preg_match('/(?<!\d)(?<begin>(?<bprefix>19|20)?\d{2})\s*年?\s*(?!\d)(?:(?:\s*[-–—\/\\\\~～到至]\s*)(?<!\d)(?<end>至今|现在|(?<eprefix>19|20)?\d{2})\s*年?(?!\d))?/u', $text, $matches)) {
            $begin = $matches['begin'] ?? '';
            $end = $matches['end'] ?? '';
            if (empty($matches['bprefix']) && empty($matches['eprefix'])) {
                $matches['bprefix'] = $matches['begin'] < 40 ? '20' : '19';
                $begin = $matches['bprefix'] . $matches['begin'];
            }
            if (empty($matches['bprefix'])) {
                $begin = $matches['eprefix'] . $matches['begin'];
                if (is_numeric($end) && $begin > $end) $begin = ($matches['eprefix'] - 1) . $matches['begin'];
            } elseif (is_numeric($end) && empty($matches['eprefix'])) {
                $end = $matches['bprefix'] . $matches['end'];
                if ($end < $begin) $end = ($matches['bprefix'] + 1) . $matches['end'];
            }
            if (empty($end)) $end = $education ? $begin + 1 : $begin;
            if ($education) return array('begin' => $begin . '-09-01', 'end' => is_numeric($end) ? $end . '-06-30' : $end);
            return array('begin' => $begin . '-01-01', 'end' => is_numeric($end) ? $end . '-01-01' : $end);
        } elseif (preg_match('/至今|现在/u', $text)) {
            $result = ['begin' => '至今', 'end' => '至今'];
            return $result;
        }
        //处理时间戳缺失的问题
        if (empty($result['begin']) || strtotime($result['begin']) < strtotime('1950-01-01') || strtotime($result['begin']) > strtotime(date('Y-12-31'))) {
            $result['begin'] = '0001-01-01';
        }
        return $result;
    }

    function formatDate($text, $delimiter = '', $monthend = FALSE) {
        if (in_array($text, array('至今', '现在'))) return $text;
        if (empty($delimiter)) {
            if (!preg_match('/((?:19|20)?\d{2})\s*年\s*([01]?\d)\s*月(?:\s*([0-3]?\d)[日号])?/u', $text, $dateArr)) return '';
            array_shift($dateArr);
        } else {
            $dateArr = explode($delimiter, $text);
        }
        if (!is_array($dateArr) || count($dateArr) < 2 || count($dateArr) > 3) return '';
        if (count($dateArr) == 2) {
            $dateArr[2] = 1;
            if (strlen($dateArr[0]) == 2) $dateArr[0] = ($dateArr[0] < 40 ? '20' : '19') . $dateArr[0];
            if ($monthend) {    //按月末记，则+1天转换为timestamp再-1
                $dateArr[1]++;
                if ($dateArr[1] == 13) {
                    $dateArr[1] = 1;
                    $dateArr[0]++;
                }
            }
        } else {
            $monthend = FALSE;
        }
        $timestamp = strtotime(implode('/', $dateArr));
        if ($monthend) $timestamp--;
        return date('Y-m-d', $timestamp);
    }

    function strToTimestamp($time) {
        if (in_array($time, array('至今', '现在'))) return PHP_INT_MAX;
        return intval(@strtotime($time));
    }

    function normalizeArrayFormat(&$target, $format) {
        if (!is_array($target)) {
            $target = array();
            return;
        }
        if (!is_array($format)) return;
        foreach ($target as &$item) {
            foreach ($format as $k => $v) {
                if (!isset($item[$k])) $item[$k] = $v;
                else $item[$k] = preg_replace('/\s*\|\s*/u', ' | ', $item[$k]);
            }
        }
        usort($target, function ($a, $b) {    //正序a-b，倒序b-a
            $deltaFrom = $this->strToTimestamp($b['time_from']) - $this->strToTimestamp($a['time_from']);
            if ($deltaFrom != 0) return $deltaFrom;
            return $this->strToTimestamp($b['time_end']) - $this->strToTimestamp($a['time_end']);
        });
    }

    function normalizeDelimiter(&$arr) {
        foreach ($arr as $key => $value) {
            if (is_array($value) && $key != 'avatar')
                $this->normalizeDelimiter($value);
            elseif (is_string($value)) {
                $tmp = preg_replace('/[^\S\r\n]*\|[^\S\r\n]*/u', ' | ', $value);
                if (!empty($tmp)) $arr[$key] = $tmp;
            }
        }
    }

    /**
     * MIME描述转为文件扩展名
     * @param string $mime
     * @return string
     */
    function mime2Ext(string $mime) {
        static $map = [
            'image/jpeg' => '.jpg',
            'image/tiff' => '.tif',
            'image/svg+xml' => '.svg',
            'image/vnd.microsoft.icon' => '.ico',
        ];
        if (!empty($map[$mime])) return $map[$mime];
        if (preg_match('/^[A-za-z]+\/(?<result>.+)$/', $mime, $matches)) return '.' . $matches['result'];
        if (preg_match('/.[^.]+$/', $mime, $matches)) return $matches[0];
        return '.' . $mime;
    }
}